﻿using System;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
using Unity.Collections.LowLevel.Unsafe;

namespace USArray
{

    public struct ArrayNoGC<T> where T : unmanaged
    {
        //存一个参考头
        public static ArrayHeader arrHeaderDef;
        static ArrayNoGC()
        {
            unsafe
            {
                var defObj = new T[1];
                GCHandle.Alloc(defObj);//死Pin 一个对象，以免参考对象没了，不一定有啥意义
                arrHeaderDef = *UnsafePtrTool.GetArrHeader(defObj);
            }
        }



        int maxlen;
        bool nofree;//不要执行free
        unsafe ArrayHeader* ptr;

        public int Length
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                unsafe
                {
                    return ptr == null ? 0 : (int)ptr->length;
                }
            }
        }

        //边界检查太多，性能就差
        public T this[int i]
        {
            get
            {
                unsafe
                {
                    if (i < 0 || i >= Length)
                    {
                        throw new IndexOutOfRangeException();
                    }
                    return ElemPtr[i];
                }
            }
            set
            {
                unsafe
                {
                    if (i < 0 || i >= Length)
                    {
                        throw new IndexOutOfRangeException();
                    }
                    ElemPtr[i] = value; ;
                }
            }
        }
        public void ResetLength(int len)
        {
            if (len < 0 || len > MaxLength)
                throw new IndexOutOfRangeException();
            unsafe
            {
                ptr->length = (IntPtr)len;
            }
        }

        public int MaxLength
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return maxlen;
            }
        }

        public unsafe ArrayHeader* HeadPtr
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {

                return ptr;
            }
        }
        public unsafe T* ElemPtr
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                if (ptr == null)
                    throw new Exception("ArrayNoGC is Null.");
                return (T*)(ptr + 1);
            }
        }

        public bool IsTemp
        {
            [MethodImpl(MethodImplOptions.AggressiveInlining)]
            get
            {
                return nofree;
            }
        }
        //临时分配的String,不用管，Unity每帧回收，无GC
        //持久化分配的就是malloc 分配，
        private ArrayNoGC(int _maxlen, bool _nofree = true, bool _withclear = false)
        {
            maxlen = _maxlen;
            nofree = _nofree;
            unsafe
            {
                var size = sizeof(ArrayHeader) + _maxlen * sizeof(T);


                //如果需要增补长度对齐，就加一个modblaue
                //var alignvalue = IntPtr.Size * 2;
                //var modvalue = (_maxlen * sizeof(T)) % alignvalue;

                ptr = (ArrayHeader*)UnsafePtrTool.Malloc(size, _nofree);
                if (_withclear)
                    UnsafeUtility.MemClear(ptr, size);
                ptr[0] = arrHeaderDef;
                ptr->length = IntPtr.Zero;
                //ptr->type = typeof(T[]).TypeHandle.Value;
            }
        }
        public static ArrayNoGC<T> Create(int _maxlen, bool _nofree = true, bool _withclear = false)
        {
            return new ArrayNoGC<T>(_maxlen, _nofree, _withclear);
        }
        //从string 创建一个StringNoGC，可以控制他的长度
        private ArrayNoGC(ref T[] buf)
        {
            unsafe
            {
                maxlen = buf.Length;
                nofree = true;
                ptr = UnsafePtrTool.GetArrHeader(buf);
            }
        }
        //不要用常量初始化，修改常量影响的是整个程序
        public static ArrayNoGC<T> ConnectTo(ref T[] buf)
        {
            return new ArrayNoGC<T>(ref buf);
        }
        public void Free()
        {
            unsafe
            {
                if (nofree || ptr == null)
                {
                    maxlen = 0;
                    ptr = null;
                }
                else
                {
                    UnsafePtrTool.FreeNoTemp(ptr);
                    maxlen = 0;
                    ptr = null;
                }
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]
        public void GetBuffer(ref T[] refstr)
        {
            unsafe
            {
                if (ptr == null)
                    refstr = null;
                refstr = UnsafePtrTool.FromPtrUnsafe<T[]>(ptr);
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]

        public T[] ToBufferNoGC()
        {
            unsafe
            {
                if (ptr == null)
                    return null;
                return UnsafePtrTool.FromPtrUnsafe<T[]>(ptr);
            }
        }
        [MethodImpl(MethodImplOptions.AggressiveInlining)]

        public T[] ToBufferNew()
        {
            unsafe
            {
                if (ptr == null)
                    return null;
                var newarr = new T[Length];
                Array.Copy(ToBufferNoGC(), newarr, Length);
                return newarr;
            }
        }

    }

}
